﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.Security;
using Microsoft.MetadirectoryServices;
using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Xml;
using System.Security.Principal; // WindowsImpersonationContext
using System.Security.Permissions; // PermissionSetAttribute

namespace FIM.Extensions.Common.Library
{
    public class PowershellUtils
    {


        public Runspace OpenRunSpace(string host, string user, SecureString password,String psAuthMechanism)
        {
            string SHELL_URI = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell";

            if (string.IsNullOrEmpty(host))
            {
                Runspace myRunSpace = RunspaceFactory.CreateRunspace();

                myRunSpace.Open();

                return myRunSpace;
            }
            else
            {
                //Create credential to use for authentication
                PSCredential credential = CreatePSCredential(user, password);

                //Validate Uri path
                if (!host.ToLower().EndsWith(":5985/wsman")) { host = host + ":5985/wsman"; }
                if (!(host.ToLower().StartsWith("http://") || host.StartsWith("https://"))) { host = "HTTP://" + host; }

                //Create connectionInfo
                Uri serverUri = new Uri(host);
                WSManConnectionInfo connectionInfo = new WSManConnectionInfo(serverUri, SHELL_URI, credential);
                switch (psAuthMechanism.ToLower())
                {
                    case "Default": connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default; break;
                    case "Basic": connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic; break;
                    case "Credssp": connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Credssp; break;
                    case "Digest": connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Digest; break;
                    case "Kerberos": connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos; break;
                    case "Negotiate": connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Negotiate; break;
                    case "NegotiateWithImplicitCredential": connectionInfo.AuthenticationMechanism = AuthenticationMechanism.NegotiateWithImplicitCredential; break;
                    default: connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default; break;
                }
                
                //Create Runspace
                Runspace myRunSpace = RunspaceFactory.CreateRunspace(connectionInfo);

                //Open Runspace
                myRunSpace.Open();

                return myRunSpace;

            }

        }

        public PSCredential CreatePSCredential(string username, SecureString password)
        {
            PSCredential credential = new PSCredential(username, password);
            return credential;
        }

        public void AddPlugins(string plugins, ref Runspace runspace)
        {
            if (!String.IsNullOrEmpty(plugins))
            {
                foreach (string plugin in plugins.Split(','))
                {
                    PowerShell myPowerShell = PowerShell.Create();
                    myPowerShell.Runspace = runspace;
                    myPowerShell.AddCommand("Add-PSSnapin");
                    myPowerShell.AddParameter("name", plugin);
                    myPowerShell.Invoke();
                    myPowerShell.Dispose();
                }
            }

        }

        public void AddScriptFromFile(string filename, ref PowerShell powershell)
        {

            using (StreamReader sr = new StreamReader(filename))
            {
                String CmdLet = sr.ReadToEnd();
                powershell.AddScript(CmdLet);
            }

        }

        public bool ExecutePowershell(PowerShell powershell, ref string errorMsg, bool treatWarningsAsError = false)
        {
            ICollection<PSObject> psResults = null;
            return ExecutePowershell(powershell, ref psResults, ref errorMsg, treatWarningsAsError);
        }

        public bool ExecutePowershell(PowerShell powershell, ref ICollection<PSObject> psResults, ref String errorMsg, bool treatWarningsAsError = false)
        {
            errorMsg = null;
            try
            {
                //Invoke powershell
                EventLog.WriteEntry("Test", "- Invoke as user : " + WindowsIdentity.GetCurrent().Name, EventLogEntryType.Information);
                psResults = powershell.Invoke();

                //Check result
                if (powershell.Streams.Error.Count > 0)
                {
                    errorMsg = "{Error}";
                    foreach (ErrorRecord err in powershell.Streams.Error) { errorMsg += ", " + err.Exception.Message; }
                    return false;
                }
                if (powershell.Streams.Warning.Count > 0)
                {
                    errorMsg = "{Warning}";
                    foreach (WarningRecord warn in powershell.Streams.Warning) { errorMsg = ", " + warn.Message; }
                    if (treatWarningsAsError) { return false; }
                }

                errorMsg = null;
                return true;
            }
            catch (Exception ex)
            {
                errorMsg = "{Exception}, " + ex.Message;
                return false;
            }

        }

        public string resolvePSFileName(string fileName)
        {

            if (string.IsNullOrEmpty(fileName)) { return null; }

            string newfile = fileName;
            string extensionDir = Utils.ExtensionsDirectory;

            if (!newfile.EndsWith(".ps1")) { newfile += ".ps1"; }

            if (File.Exists(newfile)) { return newfile; }
            if (File.Exists(extensionDir + @"\" + newfile)) { return extensionDir + @"\" + newfile; }

            return null;
        }

        public SecureString ConvertToSecureString(String plainString)
        {
            if (String.IsNullOrEmpty(plainString)) return null;
            char[] stringChars = plainString.ToCharArray();
            SecureString secureString = new SecureString();
            foreach (char c in stringChars) { secureString.AppendChar(c); }
            return secureString;
        }

    }

    public class networkUtils
    {
        // args[0] can be an IPaddress or host name. 
        public bool pingHost(string args, out string errorMsg)
        {
            try
            {
                Ping pingSender = new Ping();
                PingOptions options = new PingOptions();

                // Use the default Ttl value which is 128, 
                // but change the fragmentation behavior.
                options.DontFragment = true;

                // Create a buffer of 32 bytes of data to be transmitted. 
                string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                byte[] buffer = Encoding.ASCII.GetBytes(data);
                int timeout = 120;
                PingReply reply = pingSender.Send(args, timeout, buffer, options);
                if (reply.Status == IPStatus.Success) { errorMsg = null; return true; }
                else { errorMsg = reply.Status.ToString(); return false; }
            }
            catch (Exception ex)
            { errorMsg = ex.Message; return false; }
        }
    }

    public class xmlUtils
    {
        //Creates a new xml configuration document
        public void createXMLConfig(string path, string importDefaultPageSize, string importMaxPageSize, string exportDefaultPageSize, string exportMaxPageSize, string MAExportType)
        {
            //Skip create if xml file exists
            if (!File.Exists(path))
            {
                //Create new document
                XmlDocument xmlConfig = new XmlDocument();
                xmlConfig.AppendChild(xmlConfig.CreateXmlDeclaration("1.0", "utf-8", "yes"));
                xmlConfig.AppendChild(xmlConfig.CreateElement("FIMPowershellConfig"));

                XmlElement myElement;

                myElement = xmlConfig.CreateElement("importDefaultPageSize");
                if (!string.IsNullOrEmpty(importDefaultPageSize)) { myElement.InnerText = importDefaultPageSize; }
                xmlConfig.AppendChild(myElement);

                myElement = xmlConfig.CreateElement("importMaxPageSize");
                if (!string.IsNullOrEmpty(importDefaultPageSize)) { myElement.InnerText = importMaxPageSize; }
                xmlConfig.AppendChild(myElement);

                myElement = xmlConfig.CreateElement("exportDefaultPageSize");
                if (!string.IsNullOrEmpty(importDefaultPageSize)) { myElement.InnerText = exportDefaultPageSize; }
                xmlConfig.AppendChild(myElement);

                myElement = xmlConfig.CreateElement("exportMaxPageSize");
                if (!string.IsNullOrEmpty(importDefaultPageSize)) { myElement.InnerText = exportMaxPageSize; }
                xmlConfig.AppendChild(myElement);

                myElement = xmlConfig.CreateElement("MAExportType");
                if (!string.IsNullOrEmpty(importDefaultPageSize)) { myElement.InnerText = MAExportType; }
                xmlConfig.AppendChild(myElement);

            }

        }

    }

    public class regExUtils
    {
        public void resolveParameters(
            string parameterLine,
            out string parameterName,
            out string command,
            out string parameterBase,
            out string parameterAttribute,
            out string optionalText)
        {
            parameterName = null;
            command = null;
            parameterBase = null;
            parameterAttribute = null;
            optionalText = null;

            Regex parameterRegEx = new Regex(@"\A(?<parameterName>.*)(?<command>\x5B//(?<parameterBase>[a-öA-Ö0-9]*)/(?<parameterAttribute>[a-öA-Ö0-9]*)])(?<optionalText>.*)", RegexOptions.CultureInvariant);

            //string parameterLine = parameterMatch.Value;
            Match parameterMatch = parameterRegEx.Match(parameterLine);

            //Match match = parameterRegEx.Match(parameterLine);
            parameterName = parameterMatch.Groups["parameterName"].Value.Trim(' ').TrimStart('-');
            command = parameterMatch.Groups["command"].Value;
            parameterBase = parameterMatch.Groups["parameterBase"].Value;
            parameterAttribute = parameterMatch.Groups["parameterAttribute"].Value;
            optionalText = parameterMatch.Groups["optionalText"].Value;

        }
    }
}